package own.stu.inter.loadbalance.chooseRule;

import own.stu.inter.loadbalance.ILoadBalancer;
import own.stu.inter.loadbalance.server.ServerInstance;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import static own.stu.inter.loadbalance.constant.Constant.ROUND_ROBIN_RETRY_TIMES;

public class RoundRobinRule extends AbstractChooseRule {

    private AtomicInteger counter;

    public RoundRobinRule(ILoadBalancer lb) {
        super(lb);
        counter = new AtomicInteger(0);
    }

    public ServerInstance choose(Object key) {

        ServerInstance server = null;
        int count = 0;
        while (server == null && count++ < ROUND_ROBIN_RETRY_TIMES) {

            /*
                不单独使用 ReachableServers 的目的是
                1： allServer 不为空，reachableServers 为空，那么会获取不到服务器信息
                2： 会用其他服务器加入，或者 allServer中的服务器启动成功
             */
            List<ServerInstance> reachableServers = lb.getActiveServers();
            List<ServerInstance> allServers = lb.getAllServers();

            int upCount = reachableServers.size();
            int serverCount = allServers.size();

            if ((upCount == 0) || (serverCount == 0)) {
                logger.warn("No up servers available from loadBalancer: " + lb);
                return null;
            }

            int nextServerIndex = incrementAndGet(serverCount);
            server = allServers.get(nextServerIndex);

            if (server == null) {
                sleepAWhile(10);
                continue;
            }

            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }

            server = null;
        }

        if (count >= ROUND_ROBIN_RETRY_TIMES) {
            logger.warn("No available alive servers after {} tries from loadBalancer: {}", ROUND_ROBIN_RETRY_TIMES, lb);
        }
        return server;
    }

    private int incrementAndGet(int numServerInstances) {
        for (; ; ) {
            int current = counter.get();
            int next = (current + 1) % numServerInstances;
            if (counter.compareAndSet(current, next))
                return next;
        }
    }
}
